package fcrypt

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"log"
	"math/big"
)

func NewKeyPair() ([]byte, []byte) {
	curve := elliptic.P256()
	priKey, err := ecdsa.GenerateKey(curve, rand.Reader)
	if err != nil {
		log.Panic(err)
	}

	priKeyByte := priKey.D.Bytes()

	pubKeyByte := elliptic.Marshal(curve, priKey.PublicKey.X, priKey.PublicKey.Y)
	fmt.Println("公钥 = ", pubKeyByte)
	return priKeyByte, pubKeyByte
}

func NewKey() (ecdsa.PrivateKey, []byte) {
	curve := elliptic.P256()
	priKey, err := ecdsa.GenerateKey(curve, rand.Reader)
	if err != nil {
		log.Panic(err)
	}

	pubKeyByte := elliptic.Marshal(curve, priKey.PublicKey.X, priKey.PublicKey.Y)
	fmt.Println("公钥 = ", pubKeyByte)
	return *priKey, pubKeyByte
}

func ECDSASign(hashByte []byte, priKey ecdsa.PrivateKey) string {
	r, s, err := ecdsa.Sign(rand.Reader, &priKey, hashByte)
	if err != nil {
		return ""
	}
	strSigR := fmt.Sprintf("%x", r)
	strSigS := fmt.Sprintf("%x", s)
	if len(strSigR) == 63 {
		strSigR = "0" + strSigR
	}
	if len(strSigS) == 63 {
		strSigS = "0" + strSigS
	}

	derStr := MakeDERSignString(strSigR, strSigS)
	return derStr
}

func ECDSAVerify(hashByte []byte, pubKeyByte []byte, derSignStr string) bool {
	keyLen := len(pubKeyByte)
	if keyLen != 65 {
		fmt.Println("pub key len != 65", keyLen)
		return false
	}
	curve := elliptic.P256()
	pubKeyByte = pubKeyByte[1:]
	x := new(big.Int).SetBytes(pubKeyByte[:32])
	y := new(big.Int).SetBytes(pubKeyByte[32:])

	pubKey := ecdsa.PublicKey{curve, x, y}

	rByte, sByte := ParseDERSignString(derSignStr)
	r := new(big.Int).SetBytes(rByte)
	s := new(big.Int).SetBytes(sByte)

	return ecdsa.Verify(&pubKey, hashByte, r, s)
}

func ParseDERSignString(derStr string) (rByte, sByte []byte) {
	derByte, _ := hex.DecodeString(derStr)
	rByte = derByte[4:36]
	sByte = derByte[(len(derByte) - 33):(len(derByte) - 1)]
	return
}

func MakeDERSignString(strR, strS string) string {
	lenR := len(strR) / 2
	lenS := len(strS) / 2
	lenAll := lenR + lenS + 4

	strLenR := fmt.Sprintf("%x", int64(lenR))
	strLenS := fmt.Sprintf("%x", int64(lenS))
	strLenAll := fmt.Sprintf("%x", int64(lenAll))

	derStr := "30" + strLenAll + "02" + strLenR + strR + "02" + strLenS + strS + "01"
	return derStr
}
